import paramiko
import os
import time
from logger_config import logger


class SSHClient:
    def __init__(self, server_info):
        # 确保server_info结构正确: (id, name, ip, port, username, password, desc)
        self.server_info = server_info
        self.client = paramiko.SSHClient()
        self.client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
        self.connected = False

        # 提取服务器信息并提供默认值
        self.server_id = server_info[0] if len(server_info) > 0 else None
        self.name = server_info[1] if len(server_info) > 1 else "未知服务器"
        self.ip = server_info[2] if len(server_info) > 2 else ""
        self.port = int(server_info[3]) if len(server_info) > 3 and server_info[3] else 22
        self.username = server_info[4] if len(server_info) > 4 else ""
        self.password = server_info[5] if len(server_info) > 5 else ""

    def connect(self, max_attempts=3, delay=2):
        """连接服务器 - 支持多次尝试，增加错误信息"""
        if self.connected:
            return True

        # 验证必要的连接信息
        if not self.ip:
            error_msg = "服务器IP地址为空"
            logger.error(f"{error_msg} (服务器: {self.name})")
            return False

        if not self.username:
            error_msg = "服务器用户名为空"
            logger.error(f"{error_msg} (服务器: {self.name})")
            return False

        # 多次尝试连接
        for attempt in range(max_attempts):
            try:
                logger.info(f"尝试连接服务器 {self.name}({self.ip}:{self.port})，第 {attempt + 1}/{max_attempts} 次")

                self.client.connect(
                    hostname=self.ip,
                    port=self.port,
                    username=self.username,
                    password=self.password,
                    timeout=10,
                    allow_agent=False,  # 禁用SSH代理
                    look_for_keys=False  # 不查找本地密钥
                )

                self.connected = True
                logger.info(f"成功连接到服务器: {self.name}({self.ip}:{self.port})")
                return True

            except paramiko.AuthenticationException:
                error_msg = "认证失败 - 用户名或密码错误"
                logger.error(f"{error_msg} (服务器: {self.name})")
                # 认证错误不需要多次尝试
                return False
            except Exception as e:
                error_msg = f"连接失败: {str(e)}"
                logger.error(f"{error_msg} (服务器: {self.name}, 尝试 {attempt + 1}/{max_attempts})")

                # 如果不是最后一次尝试，等待后重试
                if attempt < max_attempts - 1:
                    time.sleep(delay)

        logger.error(f"所有尝试均失败，无法连接到服务器: {self.name}({self.ip}:{self.port})")
        return False

    def disconnect(self):
        """断开与服务器的连接"""
        if self.connected:
            try:
                self.client.close()
            except Exception as e:
                logger.warning(f"断开连接时出错: {str(e)}")
            finally:
                self.connected = False
                logger.info(f"已断开与服务器的连接: {self.name}({self.ip}:{self.port})")

    def execute_command(self, command):
        """在远程服务器上执行命令"""
        if not self.connected:
            # 尝试重新连接
            if not self.connect():
                error_msg = "未连接到服务器，且重新连接失败"
                logger.error(f"{error_msg} (服务器: {self.name})")
                return False, error_msg

        try:
            stdin, stdout, stderr = self.client.exec_command(command, get_pty=True)
            exit_status = stdout.channel.recv_exit_status()
            output = stdout.read().decode('utf-8', errors='replace')
            error = stderr.read().decode('utf-8', errors='replace')

            if exit_status != 0:
                logger.error(f"命令执行失败 (状态码: {exit_status}): {command}, 错误: {error}")
                return False, f"命令执行失败: {error}"

            logger.info(f"命令执行成功: {command}, 输出: {output[:100]}...")  # 只显示前100字符
            return True, output
        except Exception as e:
            error_msg = f"执行命令出错: {str(e)}"
            logger.error(f"{error_msg} (服务器: {self.name}, 命令: {command})")
            return False, error_msg

    def upload_file(self, local_path, remote_path):
        """上传本地文件到远程服务器"""
        if not self.connected:
            # 尝试重新连接
            if not self.connect():
                error_msg = "未连接到服务器，且重新连接失败"
                logger.error(f"{error_msg} (服务器: {self.name})")
                return False, error_msg

        # 检查本地文件是否存在
        if not os.path.exists(local_path):
            error_msg = f"本地文件不存在: {local_path}"
            logger.error(error_msg)
            return False, error_msg

        if not os.path.isfile(local_path):
            error_msg = f"不是有效的文件: {local_path}"
            logger.error(error_msg)
            return False, error_msg

        try:
            sftp = self.client.open_sftp()

            # 提取本地文件名（例如从E:/xxx.jar中获取sw-drug-ban-0.0.94.jar）
            local_filename = os.path.basename(local_path)

            # 检查远程路径是否以/结尾（目录标识），如果是则拼接文件名
            if remote_path.endswith('/'):
                # 构造完整的远程文件路径（目录+文件名）
                remote_file_path = f"{remote_path}{local_filename}"
                logger.info(f"远程路径为目录，自动拼接文件名: {remote_file_path}")
            else:
                # 否则认为remote_path是完整的文件路径
                remote_file_path = remote_path


            # 确保远程目录存在
            remote_dir = os.path.dirname(remote_path)
            mkdir_success, _ = self.execute_command(f"mkdir -p {remote_dir}")
            if not mkdir_success:
                sftp.close()
                error_msg = f"无法创建远程目录: {remote_dir}"
                logger.error(error_msg)
                return False, error_msg

            # 上传文件
            file_size = os.path.getsize(local_path)
            logger.info(f"开始上传文件: {local_path} -> {remote_file_path} (大小: {file_size} bytes)")

            sftp.put(local_path, remote_file_path, confirm=True)
            sftp.close()

            logger.info(f"文件上传成功: {local_path} -> {remote_path}")
            return True, "文件上传成功"
        except Exception as e:
            error_msg = f"文件上传失败: {str(e)}"
            logger.error(f"{error_msg} (服务器: {self.name})")
            return False, error_msg


class DeployTool:
    def __init__(self, db):
        self.db = db
        self.current_ssh_client = None

    def deploy_project(self, project_id, progress_callback=None):
        """部署项目到远程服务器，增加详细的错误处理"""
        try:
            # 1. 获取项目信息
            project = self.db.get_project_by_id(project_id)
            if not project:
                error_msg = f"项目ID {project_id} 不存在"
                logger.error(error_msg)
                return False, error_msg

            project_name = project[2]
            logger.info(f"开始部署项目: {project_name} (ID: {project_id})")

            # 2. 获取服务器信息
            server_id = project[1]
            server = self.db.get_server_by_id(server_id)
            if not server:
                error_msg = f"项目 {project_name} 关联的服务器ID {server_id} 不存在"
                logger.error(error_msg)
                return False, error_msg

            # 记录服务器信息用于调试
            logger.info(f"项目 {project_name} 关联的服务器: {server[1]}({server[2]}:{server[3]})")

            # 3. 验证服务器信息完整性
            required_fields = {
                'IP地址': server[2],
                '端口': server[3],
                '用户名': server[4],
                '密码': server[5]
            }

            for field_name, value in required_fields.items():
                if not value:
                    error_msg = f"服务器 {server[1]} 的{field_name}为空，无法部署"
                    logger.error(error_msg)
                    return False, error_msg

            # 4. 初始化SSH客户端
            self.current_ssh_client = SSHClient(server)

            # 5. 执行部署步骤
            if progress_callback:
                progress_callback(10, "正在准备部署环境...")

            # 6. 连接服务器
            if progress_callback:
                progress_callback(20, f"正在连接服务器 {server[1]}({server[2]})...")

            if not self.current_ssh_client.connect():
                error_msg = f"无法连接到服务器 {server[1]}({server[2]})"
                return False, error_msg

            if progress_callback:
                progress_callback(30, f"成功连接到服务器 {server[1]}")

            # 7. 检查并上传文件
            local_path = project[4]
            remote_path = project[5]

            if local_path and local_path.strip():
                if progress_callback:
                    progress_callback(40, f"正在准备上传文件: {os.path.basename(local_path)}")

                success, msg = self.current_ssh_client.upload_file(local_path, remote_path)
                if not success:
                    return False, f"文件上传失败: {msg}"

                if progress_callback:
                    progress_callback(60, "文件上传成功")
            else:
                if progress_callback:
                    progress_callback(60, "无需上传文件，跳过上传步骤")
                logger.info(f"项目 {project_name} 未配置本地文件路径，跳过上传步骤")

            # 8. 执行停止命令
            stop_command = project[7]
            if stop_command and stop_command.strip():
                if progress_callback:
                    progress_callback(70, "正在执行停止命令...")

                success, msg = self.current_ssh_client.execute_command(stop_command)
                if not success:
                    logger.warning(f"停止命令执行失败，但继续部署: {msg}")
                    if progress_callback:
                        progress_callback(75, f"停止命令执行警告: {msg[:50]}...")

            # 9. 执行启动命令
            start_command = project[6]
            if start_command and start_command.strip():
                if progress_callback:
                    progress_callback(80, "正在执行启动命令...")

                success, msg = self.current_ssh_client.execute_command(start_command)
                if not success:
                    return False, f"启动命令执行失败: {msg}"

                if progress_callback:
                    progress_callback(90, "启动命令执行成功")
            else:
                logger.warning(f"项目 {project_name} 未配置启动命令")
                if progress_callback:
                    progress_callback(90, "未配置启动命令，跳过启动步骤")

            # 10. 部署完成
            if progress_callback:
                progress_callback(100, "部署完成")

            logger.info(f"项目部署成功: {project_name}@{server[1]}")
            return True, "部署成功"

        except Exception as e:
            error_msg = f"部署过程中发生错误: {str(e)}"
            logger.error(f"{error_msg}", exc_info=True)  # 记录完整堆栈信息
            return False, error_msg
        finally:
            # 确保断开连接
            if self.current_ssh_client:
                self.current_ssh_client.disconnect()
